<?php

class Storage_Adapter_Ssh2 extends Storage_Adapter
{
	/**
	 * @var resource
	 */
	protected $_stream = NULL;

	/**
	 * @var resource
	 */
	protected $_session = NULL;

	/**
	 * @var string
	 */
	protected $_host = 'localhost';

	/**
	 * @var int
	 */
	protected $_port = 22;

	/**
	 * @var string
	 */
	protected $_username = '';

	/**
	 * @var string
	 */
	protected $_password = '****';

	function __construct($id, $config)
	{
		$this -> _id = $id;
		$this -> _host = $config['host'];
		$this -> _port = $config['port'];
		$this -> _username = $config['username'];
		$this -> _password = $config['password'];
		$this -> _basePath = rtrim($config['basePath'], '/') . '/';
		$this -> _baseUrl =  rtrim($config['baseUrl'], '/') . '/';

		if (!function_exists('ssh2_sftp_mkdir'))
		{
			throw new Storage_Exception("require php ssh2 extension!");
		}
	}

	/**
	 * connect ftp resource handle
	 * @return void
	 */
	function connect()
	{
		if ($this -> _session and is_resource($this -> _session))
		{
			return;
		}

		$this -> _session = @ssh2_connect($this -> _host, $this -> _port);

		if (NULL == $this -> _session)
		{
			throw new Storage_Exception('could not connect to {host}:{port}', array(
				'{host}' => $this -> _host,
				'{port}' => $this -> _port
			));
		}

		@ssh2_auth_password($this -> _session, $this -> _username, $this -> _password);
	}

	function getSession()
	{
		$this -> _session or $this -> connect();
		return $this -> _session;
	}

	/**
	 * disconnect session
	 * @return void
	 */
	function disconnect()
	{
		$this -> _stream = NULL;
		$this -> _session = NULL;
	}

	/**
	 * @return resource
	 */
	function getStream()
	{
		if (null == $this -> _stream)
		{
			$this -> _session or $this -> connect();
			$this -> _stream = @ssh2_sftp($this -> _session);
			if (!$this -> _stream)
			{
				throw new Storage_Exception('could not init ssh2 stream to {host}:{port}', array(
					'{host}' => $this -> _host,
					'{port}' => $this -> _port,
				));
			}
		}
		return $this -> _stream;
	}

	/**
	 * @param string $command
	 * @param bool $force optional default TRUE
	 * @return TRUE|FALSE
	 * @throws Storage_Exception
	 */
	public function command($command, $force = TRUE)
	{
		if (!@ftp_site($this -> getStream(), $command))
		{
			if ($force)
			{
				throw new Storage_Exception('Unable to execute SITE {cmd}', array('{cmd}' => $command));
			}
			return FALSE;
		}
		return TRUE;
	}

	/**
	 * @param string $path relative path
	 * @param string $local_file  absolute local file path
	 * @return true|false
	 * @throws Storage_Exception
	 */
	function put($relative_path, $local_file, $force = TRUE)
	{

		if (!file_exists($local_file))
		{
			if ($force)
			{
				throw new Storage_Exception('file does not exists {file}', array('{file}' => $local_file, ));
			}
		}

		if (!$this -> makeDir(dirname($relative_path)))
		{

		}

		$path = $this -> mapPath($relative_path);

		if (!@ssh2_scp_send($this -> getSession(), $local_file, $path, 0666))
		{
			if ($force)
			{
				throw new Storage_Exception('could not put "{from}" to {to}', array(
					'{from}' => $local_file,
					'{to}' => $path,
				));
			}
			return FALSE;
		}

		return TRUE;
	}

	/**
	 * copy content of relative path to local file and set mode
	 * @param string $local_file absolute file path
	 * @param string $relative_path
	 * @param int mode
	 * @return TRUE|FALSE
	 * @throws Storage_Exception
	 */
	public function get($relative_path, $local_file, $force = TRUE)
	{

		self::_makeLocalDir(dirname($local_file));

		$path = $this -> mapPath($relative_path);

		// Error
		if (!@ssh2_scp_recv($this -> getSession(), $path, $local_file))
		{
			if ($force)
			{
				throw new Storage_Exception('Unable to get "{from}" to "{to}"', array(
					'{from}' => $path,
					'{to}' => $local_file
				));
			}
			return FALSE;
		}

		return TRUE;
	}

	function putContents($relative_path, $data, $force = TRUE)
	{

	}

	function getContents($relative_path, $force = TRUE)
	{

	}

	/**
	 * @link http://www.php.net/manual/en/function.ssh2-sftp-unlink.php
	 * delete relative path
	 * @param $force  optional = FALSE
	 * @param string $relative_path
	 */
	function delete($relative_path, $force = FALSE)
	{
		$path = $this -> getPath($relative_path);

		if (!@ssh2_sftp_unlink($this -> getStream(), $path))
		{
			if ($force)
			{
				throw new Storage_Exception('could not remove {from}', array('{from}' => $path, ));
			}
			return FALSE;
		}
		return TRUE;
	}

	public function isFile($relative_path)
	{
		$stream = 'ssh2.sftp://' . $this -> getStream() . $this -> mapPath($relative_path);
		return file_exists($stream);
	}

	/**
	 * @param string $relative_path
	 */
	public function isDir($relative_path)
	{
		$stream = 'ssh2.sftp://' . $this -> getStream() . $this -> mapPath($relative_path);
		return is_dir($stream);
	}

	/**
	 * @link http://www.php.net/manual/en/function.ssh2-sftp-mkdir.php
	 * @param string $relative_path
	 * @param $mode
	 * @return TRUE|FALSE
	 */
	function makeDir($relative_path, $mode = 0755, $force = TRUE)
	{
		// Check if the directory already exists
		if ($this -> isDir($relative_path))
		{
			return TRUE;
		}

		$path = $this -> mapPath($relative_path);

		if (!@ssh2_sftp_mkdir($this -> getStream(), $path, 0777, 1))
		{
			if ($force)
			{
				throw new Storage_Exception('could not make dir "{dir}"', array('{dir}' => $path));
			}
			return FALSE;
		}
		return TRUE;
	}

	/**
	 * @link http://www.php.net/manual/en/function.ssh2-sftp-rmdir.php
	 * @param string $relative_path
	 * @param bool $force optional FALSE
	 * @return bool
	 * @throws Storage_Exception
	 */
	function removeDir($relative_path, $force = FALSE)
	{
		$path = $this -> mapPath($relative_path);
		if (!@ssh2_sftp_rmdir($this -> getStream(), $path))
		{
			if ($force)
			{
				throw new Storage_Exception('could not remove "{dir}"', array('{dir}' => $path));
			}
			return FALSE;
		}
		return TRUE;
	}

}
